module CPU (
    input clk,         // 时钟信号
    input reset,        // 复位信号
    input [3:0] switch,     // 开关输入，由外部输入。
    output [31:0] result    // IOManager的输出结果
);

    // 定义连接各模块的wire信号
    wire [31:0] pc;        // 当前指令地址，由PC模块输出，连接到ROM模块的输入
    wire [31:0] next_pc;   // 下一条指令地址，由CU和ALU计算后输出，连接到PC模块的输入
    wire [31:0] ins;       // 当前指令，由ROM模块输出，连接到ID模块的输入
    wire [31:0] rdata1;    // 第一个读寄存器的数据，由REGs模块输出，连接到ALU模块的输入
    wire [31:0] rdata2;    // 第二个读寄存器的数据，由REGs模块输出，连接到ALU和RAM模块的输入
    wire [31:0] alu_result;// ALU计算结果，由ALU模块输出，连接到RAM和CU模块的输入
    wire [31:0] io_data;   // IOManager读取的数据，由IOManager模块输出，连接到寄存器数据路径的选择
    wire [11:0] imm12;     // 12位立即数，由ID模块输出，连接到ALU模块的输入
    wire [19:0] imm20;     // 20位立即数，由ID模块输出，连接到ALU模块的输入
    wire [4:0] rs1;        // 第一个源寄存器地址，由ID模块输出，连接到REGs模块的输入
    wire [4:0] rs2;        // 第二个源寄存器地址，由ID模块输出，连接到REGs模块的输入
    wire [4:0] rd;         // 目标寄存器地址，由ID模块输出，连接到REGs模块的输入
    wire [6:0] opcode;     // 操作码，由ID模块输出，连接到CU模块的输入
    wire [2:0] func;       // 功能码，由ID模块输出，连接到CU模块的输入
    wire dis;              // 区分码，由ID模块输出，连接到CU模块的输入
    wire we;               // 写使能信号，由CU模块输出，连接到REGs和RAM模块的输入
    wire isimm;            // b是否是立即数，由CU模块输出，连接到ALU模块的输入
    wire is20;             // b是否是20位立即数，由CU模块输出，连接到ALU模块的输入
    wire isfpc;            // a是否来源于pc，由CU模块输出，连接到ALU模块的输入
    wire isfm;             // rwdata是否来源于mem，由CU模块输出，连接到数据选择器
    wire ispc4;            // rwdata是否来源于pc+4，由CU模块输出，连接到数据选择器
    wire c;                // 条件信号，由ALU模块输出，连接到CU模块的输入
    wire [3:0] alu_op;     // ALU运算类型，由CU模块输出，连接到ALU模块的输入
    wire [3:0] mm;         // RAM写方式控制，由CU模块输出，连接到RAM模块的输入
    wire [1:0] pcs;        // 下一条指令地址来源方式，由CU模块输出，连接到PC选择器



    // PC模块实例化
    // PC模块管理程序计数器（PC），在复位时将PC设置为0，否则将其更新为next_pc。
    PC pc_module (
        .reset(reset),      // 复位信号，高电平时将PC重置为0
        .clk(clk),          // 时钟信号
        .next_addr(next_pc),// 下一条指令地址，由CU模块和ALU模块计算得到
        .insAddr(pc)        // 当前指令地址，输出到ROM模块和CU模块
    );

    // ROM模块实例化
    // ROM模块存储程序指令，根据PC提供的地址读取指令。
    ROM rom_module (
        .maddr(pc[5:0]),         // 当前指令地址，由PC模块提供
        .mdata(ins)         // 当前指令，输出到ID模块
    );

    // ID模块实例化
    // ID模块解析当前指令，提取操作码、源寄存器地址、目标寄存器地址和立即数等字段。
    ID id_module (
        .ins(ins),          // 当前指令，由ROM模块提供
        .rs1(rs1),          // 第一个源寄存器地址，输出到REGs模块
        .rs2(rs2),          // 第二个源寄存器地址，输出到REGs模块
        .rd(rd),            // 目标寄存器地址，输出到REGs模块
        .opcode(opcode),    // 操作码，输出到CU模块
        .imm12(imm12),      // 12位立即数，输出到ALU模块
        .func(func),        // 功能码，输出到CU模块
        .imm20(imm20),      // 20位立即数，输出到ALU模块
        .dis(dis)           // 区分码，输出到CU模块
    );

    // REGs模块实例化
    // REGs模块管理寄存器文件，可以在时钟上升沿同时从两个寄存器中读取数据，并在写使能信号有效时将数据写入指定寄存器。
    REGs regs_module (
        .clk(clk),          // 时钟信号
        .raddr1(rs1),       // 第一个读寄存器地址，由ID模块提供
        .rdata1(rdata1),    // 第一个读寄存器数据，输出到ALU模块
        .raddr2(rs2),       // 第二个读寄存器地址，由ID模块提供
        .rdata2(rdata2),    // 第二个读寄存器数据，输出到ALU模块和IOManager模块
        .waddr(rd),         // 写寄存器地址，由ID模块提供
        .wdata(isfm ? io_data : alu_result), // 写寄存器数据，由ALU模块或IOManager提供
        .we(we)             // 写使能信号，由CU模块提供
    );

    // CU模块实例化
    // CU模块（控制单元）根据操作码和功能码生成各种控制信号，以指导其他模块的操作。
    CU cu_module (
        .opcode(opcode),    // 操作码，由ID模块提供
        .func(func),        // 功能码，由ID模块提供
        .dis(dis),          // 区分码，由ID模块提供
        .c(c),              // 条件信号，由ALU模块提供
        .pcs(pcs),          // 下一条指令地址来源方式，输出到PC地址选择器
        .rwe(we),           // 寄存器写使能信号，输出到REGs模块
        .mwe(mwe),          // RAM写使能信号，输出到IOManager模块
        .mm(mm),            // RAM写方式控制信号，输出到IOManager模块
        .aluOP(alu_op),     // ALU运算类型，输出到ALU模块
        .isimm(isimm),      // 是否使用12位立即数，输出到ALU模块
        .is20(is20),        // 是否使用20位立即数，输出到ALU模块
        .isfpc(isfpc),      // 是否使用PC作为ALU输入a，输出到ALU模块
        .isfm(isfm),        // 是否使用内存数据作为rwdata，输出到数据选择器
        .ispc4(ispc4)       // 是否使用PC+4作为rwdata，输出到数据选择器
    );

    // ALU模块实例化
    // ALU模块（算术逻辑单元）执行各种算术和逻辑操作。
    ALU alu_module (
        .a(is20 ? 32'b0 : (isfpc ? pc : rdata1)),          // ALU输入a，根据控制信号选择是使用PC还是rdata1
        .b(isimm ? {{20{imm12[11]}}, imm12} : (is20 ? {{12{imm20[19]}}, imm20} : rdata2)),  // ALU输入b，根据控制信号选择是使用符号扩展后的12位立即数、符号扩展后的20位立即数还是rdata2
        .op(alu_op),                      // ALU操作码，由CU模块提供
        .f(alu_result),                   // ALU计算结果，输出到REGs模块和IOManager模块
        .c(c)                             // 进位标志或条件信号，输出到CU模块
    );
    
    // IOManager模块实例化
    // IOManager模块处理输入输出和数据存储，根据地址进行选择。
    IOManager io_manager (
        .maddr(alu_result[7:0]), // 内存地址，由ALU计算得到，取低8位
        .mm(mm),            // 内存操作模式，由CU模块提供
        .clk(clk),               // 时钟信号
        .we(mwe),                // 写使能信号，由CU模块提供
        .wdata(rdata2),          // 写入数据，由REGs模块提供
        .data(io_data),          // 读取数据，输出到数据选择器
        .switch(switch),         // 输入开关，由外部输入
        .result(result)          // 输出结果
    );

    // 下一个PC地址计算
    assign next_pc = (pcs == 2'b00) ? (pc + 4) :
                     (pcs == 2'b01) ? (pc + imm12) :
                     (pcs == 2'b10) ? alu_result : 32'b0;

    reg [3:0] count;
    reg [1:0] status;

    always @ (posedge clk) begin
        if (reset) begin
            count <= 4'b0010;
            status <= 2'b00;
        end
        else 
            case(status)
                2'b00: begin
                    isf <= 1'b0;
                    wa <= 5'b00001;
                    we <= 1'b1;
                    status <= 2'b01;
                end
                2'b01: begin
                    isf <= 1'b0;
                    wa <= 5'b00010;
                    we <= 1'b1;
                    ra1 <= 5'b00001;
                    ra2 <= 5'b00010;
                    status <= 2'b10;
                end
                2'b10: begin
                    isf <= 1'b1;
                    wa <= ra2 + 5'b00001;
                    we <= 1'b1;
                    count <= count + 4'b0001;
                    status <= 2'b11;
                end
                2'b11: begin
                    if(count < n) begin
                        ra1 <= ra1 + 5'b00001;
                        ra2 <= ra2 + 5'b00001;
                        we <= 1'b0;
                        status <= 2'b10;
                    end
                end
                default: begin
                    status <= 2'b00;
                end
            endcase
    end
    
endmodule
